/**
 *
 *  Copyright (c) 2011, AstroDAbis
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without modification,
 *  are permitted provided that the following conditions are met:
 *
 *      * Redistributions of source code must retain the above copyright notice,
 *        this list of conditions and the following disclaimer.
 *      * Redistributions in binary form must reproduce the above copyright notice,
 *        this list of conditions and the following disclaimer in the documentation
 *        and/or other materials provided with the distribution.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 *  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 *  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 *  OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
package org.jiscinvolve.astrodabis.barberry.control.registry;

import java.net.URI;
import java.net.URL;
import java.net.URISyntaxException;
import java.net.MalformedURLException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.validation.Valid;

import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.validation.BindingResult;

import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.context.request.WebRequest;

import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ModelAttribute;

import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional; 

import org.jiscinvolve.astrodabis.barberry.control.Paths;
import org.jiscinvolve.astrodabis.barberry.control.ObjectData;
import org.jiscinvolve.astrodabis.barberry.control.RequestData;
import org.jiscinvolve.astrodabis.barberry.control.BaseController;

import org.jiscinvolve.astrodabis.barberry.common.core.Tag;
import org.jiscinvolve.astrodabis.barberry.common.core.Account;

import org.jiscinvolve.astrodabis.barberry.common.core.Identifier;
import org.jiscinvolve.astrodabis.barberry.common.core.Identified;
import org.jiscinvolve.astrodabis.barberry.common.core.SimpleIdent;

import org.jiscinvolve.astrodabis.barberry.common.data.TapTable;
import org.jiscinvolve.astrodabis.barberry.common.data.TapColumn;
import org.jiscinvolve.astrodabis.barberry.common.data.TapService;

import org.jiscinvolve.astrodabis.barberry.registry.RegistryHarvester;

import org.jiscinvolve.astrodabis.barberry.common.core.exception.*;

/**
 *
 */
@Controller
@RequestMapping(Paths.REGISTRY_SCAN)
public class HarvestRegController
extends BaseController
    {

    /**
     * Our debug logger.
     * 
     */
    private static Log log = LogFactory.getLog(HarvestRegController.class);

    /**
     * The form page.
     * 
     */
    public static final String FORM_PAGE = "registry/registry-scan" ;

    /**
     * Form attribute name.
     * 
     */
    public static final String FORM_DATA = "form" ;


    public static class Bean
        {
        public Bean()
            {
            }

        private String url ; 
        public String getUrl()
            {
            return this.url;
            }
        public void setUrl(String url)
            {
            this.url = url;
            }

        public enum Status
            {
            DONE(),
            FAILED();
            }        

        private Status status ; 
        public Status getStatus()
            {
            return this.status;
            }
        public void setStatus(Status status)
            {
            this.status = status;
            }
        }

    /**
     * Create our RequestData for this request.
     *
     */
    @ModelAttribute(RequestData.MODEL_ATTRIB)
    public RequestData data(
        WebRequest request
        ){
        return new RequestData(
            request,
            womble,
            actor(
                request
                )
            );
        }

    /**
     * Get the form.
     * 
     */
	@RequestMapping(method=RequestMethod.GET)
	public ModelAndView page(
	    @ModelAttribute(RequestData.MODEL_ATTRIB) RequestData data,
	    ModelAndView model
	    ){
        //
        // If we are not logged in.
        if (data.actor() == null)
            {
            throw new PermissionException();
            }
        //
        // If we are logged in.
        else {
            model.addObject(
                FORM_DATA,
                new Bean()
                );
            model.setViewName(
    		    FORM_PAGE
                );
            return model ;
            }
        }

    /**
     * Post the form.
     * 
     */
	@RequestMapping(method=RequestMethod.POST)
	public ModelAndView post(
	    @ModelAttribute(FORM_DATA) @Valid Bean bean, BindingResult result,
	    @ModelAttribute(RequestData.MODEL_ATTRIB) RequestData data,
	    ModelAndView model
	    ){
        //
        // If we are not logged in.
        if (data.actor() == null)
            {
            throw new PermissionException();
            }
        //
        // If we are logged in.
        else {
            //
            // If the form data is not valid, re-display the form page.
		    if (result.hasErrors())
		        {
                log.debug("Input has errors");
                for(FieldError error : result.getFieldErrors())
                    {
                    log.debug("  Error [" + error.getObjectName() + "][" + error.getField() + "][" + error.getDefaultMessage() + "]");
                    }
        		model.setViewName(
                    FORM_PAGE
                    );
		        }
            //
            // If the input is valid.
            else {

                //
                // Nowhere else to go yet. ..
                model.setViewName(
        		    FORM_PAGE
                    );

                //
                // Scan the registry ...
                try {
                    RegistryHarvester harvey = new RegistryHarvester();
                    harvey.harvest(
                        bean.getUrl()
                        );
                    for(RegistryHarvester.Service regService : harvey.services())
                        {
                        try {
                            log.debug(
                                regService
                                );
                            TapService tapService = womble().services().create(
                                new URI(
                                    regService.ivorn()
                                    ),
                                regService.title()
                                );
                            if (tapService.name() == null)
                                {
                                tapService.name(
                                    regService.title()
                                    );
                                }
                            if (tapService.text() == null)
                                {
                                tapService.text(
                                    regService.title()
                                    );
                                }
                            if (tapService.endpoint() == null)
                                {
                                try {
                                    tapService.endpoint(
                                        new URL(
                                            regService.tap().url()
                                            )
                                        );
                                    }
                                catch (MalformedURLException ouch)
                                    {
                                    log.warn("Invalid endpoint URL in registry data [" + regService.tap().url() + "]");
                                    }
                                }
                            womble().services().update(
                                tapService
                                );

                            log.debug(
                                tapService
                                );

                            for (RegistryHarvester.Service.Schema regSchema : regService.schemas())
                                {
                                log.debug(
                                    regSchema
                                    );
                                for (RegistryHarvester.Service.Schema.Table regTable : regSchema.tables())
                                    {
                                    log.debug(
                                        regTable
                                        );
                                    TapTable tapTable = tapService.tables().create(
                                        regTable.name()
                                        );
                                    log.debug(
                                        tapTable
                                        );

                                    for (RegistryHarvester.Service.Schema.Table.Column regColumn : regTable.columns())
                                        {
                                        log.debug(
                                            regColumn
                                            );
                                        TapColumn tapColumn = tapTable.columns().create(
                                            regColumn.name()
                                            );
                                        log.debug(
                                            tapColumn
                                            );
                                        }
                                    }
                                }
                            }
                        catch (URISyntaxException ouch)
                            {
                            log.warn("Invalid service URI in registry data [" + regService.ivorn() + "]");
                            }
                        }
                    bean.setStatus(
                        Bean.Status.DONE
                        );
                    }
                //
                // Anything goes wrong, add an error and re-display the form page.
                catch (Throwable ouch)
                    {
                    log.error("Error during registry scan [" + ouch.getClass().getName() + "][" + ouch.getMessage() + "]");
                    result.addError(
                        new ObjectError(
                            FORM_DATA,
                            "Oops, something went wrong. Please try again later"
                            )
                        );
                    bean.setStatus(
                        Bean.Status.FAILED
                        );
            		model.setViewName(
                        FORM_PAGE
                        );
                    }
                }
            }
        return model ;
        }
    }

